/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package matrizes;

public class Matrix
   implements Cloneable // We need this in order to allow others to clone our Matrix (see JDK docs).
{
   static final boolean __MATRIX_DEBUG = true; // Can use if we wish to output data for debugging.

   //
   // DATA
   //
   protected int rows;                 // Number of rows in the matrix.
   protected int cols;                 // Number of columns in the matrix.

   protected double elements[][];      // Two dimensional array to store the elements.

   //
   // CONSTRUCTORS
   //

   Matrix(int r, int c)
   {
      if ( __MATRIX_DEBUG )
      {
         System.out.println("DEBUG - Matrix(int r, int c) called.");
      }

      //
      // Validate parameters... should be greater than 0.
      if ( r < 1 || c < 1 )
      {
         System.out.println("Matrix constructor. Invalid rows or cols");

         System.exit(0);
      }
      //
      // Use rows and columns to create a 2-d array of given size.
      rows = r;
      cols = c;

      elements = new double[rows][cols];
   }

   Matrix(int r, int c, double val)
   {
      this(r, c); // Calls 'Matrix(int r, inr c)' (The above constructor - This MUST be the FIRST LINE).

      if ( __MATRIX_DEBUG )
      {
         System.out.println("DEBUG - Matrix(int r, int c, double val) called.");
      }
      //
      // For each row...
      for ( int i = 0; i < rows; i++ )
      {
         //
         // ..for each element in the row..
         for ( int j = 0; j < cols; j++ )
         {
            elements[i][j] = val;
         }
      }
   }

   //
   // API METHODS
   //

   public int getRows()
   {
      return rows;
   }

   public int getCols()
   {
      return cols;
   }

   public boolean setElement(int r, int c, double val)
   {
      //
      // Validate the parameters.
      if ( r < 1 || r > rows || c < 1 || c > cols ) return false;
      //
      // The API defines a one-based matrix, so subtract 1 to get correct element.
      elements[r-1][c-1] = val;

      return true;
   }

   public double getElement(int r, int c)
   {
      //
      // Validate the parameters.
      if ( r < 1 || r > rows || c < 1 || c > cols ) return 0.0;
      //
      // The API defines a one-based matrix, so subtract 1 to get correct element.
      return elements[r-1][c-1];
   }

   public Matrix add(Matrix m)
   {
      return addOrSubtract(m, false);
   }

   public Matrix sub(Matrix m)
   {
      return addOrSubtract(m, true);
   }

   public String toString()
   {
      StringBuffer buf = new StringBuffer(80);

      for ( int r = 0; r < rows; r++ )
      {
         buf.append((r==0 ? "[" : ", "));

         for ( int c = 0; c < cols; c++ )
         {
            buf.append((c==0 ? "[" : ", "));
            buf.append(elements[r][c]);
         }
         buf.append("]");
      }
      buf.append("]");

      return buf.toString();
   }

   public Matrix clone()
   {
      //
      // In order to create a copy of our matrix, we must create a new matrix ourselves and then
      // set the value for each element in the new matrix.

      Matrix newMatrix = new Matrix(rows, cols);

      for ( int r = 0; r < rows; ++r )
      {
         for ( int c = 0; c < newMatrix.cols; ++c )
         {
            newMatrix.elements[r][c] = elements[r][c];
         }
      }
      return newMatrix;
   }

   //
   // HELPER METHODS
   //
   private Matrix addOrSubtract(Matrix m, boolean amISubtracting)
   {
      //
      // Validate passed matrix.  Validate it is the same size.
      if ( this.rows != m.rows || this.cols != m.cols )
      {
         System.out.println("Matrix method add. Invalid rows or cols");

         System.exit(0);
      }
      //
      // Create our new matrix.
      Matrix newMatrix = new Matrix(rows, cols);
      //
      // Add the matrix passed to out matrix.

      //
      // For each row in out new matrix...
      for ( int i = 0; i < rows; i++ )
      {
         //
         // For each element in the current row...
         for ( int j = 0; j < cols; j++ )
         {
            double newVal = m.elements[i][j];

            if ( amISubtracting )
            {
               newVal *= -1;
            }

            //
            // Add the correspondong elements.
            newMatrix.elements[i][j] = this.elements[i][j] + newVal;

            //
            // If we are debugging, print the new value.
            if ( __MATRIX_DEBUG )
            {
               System.out.println("DEBUG - addOrSubtract:: Row: " + i + " Col: " + j + " Val: " + newMatrix.elements[i][j]);
            }
         }
      }
      //
      // Return the new matrix.
      return newMatrix;
   }
}